package cm.tl.pms.common.config;

import cm.tl.pms.annotation.ParamLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 方法入参的日志切面方法
 *
 * @author w7h1te_ywq
 * @version 1.0.0
 * @date 2023-11-01 18:25
 */
@Aspect
@Component
@Order(1)
public class ParamLogAspect {

    public static final Logger logger = LoggerFactory.getLogger(ParamLogAspect.class.getName());

    @Pointcut("@within(org.springframework.web.bind.annotation.RestController) || @annotation(org.springframework.web.bind.annotation.RestController)")
    public void restControllerPointcut() {
    }


    @Before("restControllerPointcut() && @annotation(paramLog)")
    public void logMethodParams(JoinPoint joinPoint, ParamLog paramLog) throws NoSuchMethodException {
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getTarget().getClass().getSimpleName();

        Method method = getMethod(joinPoint, paramLog.methodName());
        if (method != null) {
            // 获取方法的参数名和类型
            String[] paramNames = getParameterNames(method);
            Class<?>[] paramTypes = method.getParameterTypes();

            // 获取方法的参数值，并构造参数列表字符串
            StringBuilder sb = new StringBuilder();
            sb.append("Class: ").append(className).append(", Method: ").append(method.getName()).append(", Params: [");
            for (int i = 0; i < paramNames.length; i++) {
                if (i > 0) {
                    sb.append(", ");
                }
                String paramName = paramNames[i];
                Object paramValue = joinPoint.getArgs()[i];

                // 判断参数值是否为包含继承关系的类型
                if (isInheritedClass(paramValue)) {
                    // 递归调用方法，打印所有继承的父类字段信息
                    sb.append(printInheritedFieldsAsString(paramValue));
                } else {
                    // 打印普通参数的名称和值
                    sb.append(paramName).append("=").append(paramValue);
                }
            }
            sb.append("]");

            // 打印参数列表字符串
            logger.info(sb.toString());
        }
    }

    /**
     * 获取所有继承的父类字段信息，并返回字符串形式
     *
     * @param obj 对象
     * @return 所有继承的父类字段信息
     */
    private String printInheritedFieldsAsString(Object obj) {
        // 获取对象的类
        Class<?> clazz = obj.getClass();

        // 遍历类及其父类的字段，拼接字段名和值为字符串
        StringBuilder sb = new StringBuilder();
        while (clazz != null) {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                try {
                    Object fieldValue = field.get(obj);
                    if (sb.length() > 0) {
                        sb.append(", ");
                    }
                    sb.append(field.getName()).append("=").append(fieldValue);
                } catch (IllegalAccessException e) {
                    // 处理异常
                    logger.error(" 字段的访问权限不足 ", e);
                }
            }
            clazz = clazz.getSuperclass();
        }
        return sb.toString();
    }


    /**
     * 判断参数值是否为包含继承关系的类型
     *
     * @param paramValue 参数值
     * @return 是否为包含继承关系的类型
     */
    private boolean isInheritedClass(Object paramValue) {
        if (paramValue != null && paramValue.getClass().getSuperclass() != null) {
            return true;
        }
        return false;
    }

    /**
     * 打印所有继承的父类字段信息
     *
     * @param obj 对象
     */
    private void printInheritedFields(Object obj) {
        // 获取对象的类
        Class<?> clazz = obj.getClass();

        // 遍历类及其父类的字段，打印字段名和值
        while (clazz != null) {
            Field[] fields = clazz.getDeclaredFields();

            for (Field field : fields) {
                field.setAccessible(true);
                try {
                    Object fieldValue = field.get(obj);
                    logger.info("Param: " + field.getName() + " = " + fieldValue);
                } catch (IllegalAccessException e) {
                    // 处理异常
                    logger.error(" 字段的访问权限不足 ", e);
                }
            }

            clazz = clazz.getSuperclass();
        }
    }


    @AfterReturning(pointcut = "restControllerPointcut() && @annotation(paramLog)", returning = "returnValue")
    public void logMethodReturn(JoinPoint joinPoint, ParamLog paramLog, Object returnValue) throws NoSuchMethodException {
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getTarget().getClass().getSimpleName();

        Method method = getMethod(joinPoint, paramLog.methodName());
        if (method != null) {
            // 打印方法名称和类名称
            logger.info("Class: " + className + ", Method: " + method.getName());

            // 打印返回值
            logger.info("Return: " + returnValue);
        }
    }

    private Method getMethod(JoinPoint joinPoint, String methodName) throws NoSuchMethodException {
        Method[] methods = joinPoint.getTarget().getClass().getMethods();
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                return method;
            }
        }
        return null;
    }

    private String[] getParameterNames(Method method) {
        // 实现获取方法参数名的逻辑
        // 这里仅为示例，实际项目中可以使用反射库或字节码操作库来获取参数名
        return Arrays.stream(method.getParameters())
                .map(parameter -> parameter.getName())
                .toArray(String[]::new);
    }
}